package br.edu.ufcg.splab.io;


import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.output.XMLOutputter;

import br.edu.ufcg.splab.core.InterfaceEdge;
import br.edu.ufcg.splab.core.edges.EdgeType;
import br.edu.ufcg.splab.generator.TestCase;
import br.edu.ufcg.splab.generator.TestSuite;
import br.edu.ufcg.splab.util.MapTestSuite;


/**
 * TestLink is a tool created to support the test execution process. In a
 * TestLink project is possible create test suites, test cases and manage their
 * execution. This class receives a TestSuite object and writes a .xml
 * representation for TestLink importing purposes, following the structure
 * defined by the TestlinkSchema.xsd file.
 * 
 * @author Joao Felipe
 * 
 */
public class TestLinkXMLWriter{
	
	/**
	 * Converts the given test suite in a XML compatible with the TestLink tool. 
	 * @param suite The TestSuite object to be converted. 
	 * @param filepath The path of the resultant XML file.
	 * @param mapTS The map of the test cases.
	 */
	public void write (TestSuite suite, String filepath, MapTestSuite mapTS){
		Document xmlRepresentation = this.convertTestSuiteToXML(suite, mapTS);
		XMLOutputter outputter = new XMLOutputter();
		try {
			FileWriter writer = new FileWriter(filepath);
			outputter.output(xmlRepresentation, writer);
			writer.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * Converts the given test suite in a XML compatible with the TestLink tool. 
	 * @param suite The TestSuite object to be converted. 
	 * @param filepath The path of the resultant XML file.
	 * @param mapTS The map of the test cases.
	 */
	public void write (TestSuite suite, String filepath){
		MapTestSuite mapTS = new MapTestSuite(suite);
		Document xmlRepresentation = this.convertTestSuiteToXML(suite, mapTS);
		XMLOutputter outputter = new XMLOutputter();
		try {
			FileWriter writer = new FileWriter(filepath);
			outputter.output(xmlRepresentation, writer);
			writer.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * Convert a single TestSuite object into a equivalent XML tag in Testlink format.
	 * @param suite The test suite to be converted.
	 * @param mapTS The map of the test cases.
	 * @return The correspondent XML tag, according to the defined schema. 
	 */
	private Document convertTestSuiteToXML(TestSuite suite, MapTestSuite mapTS) {
		Element testSuiteTag = new Element("testsuite");
		testSuiteTag.setAttribute("name", "TS");
		testSuiteTag.addContent(new Element("node_order"));
		testSuiteTag.addContent(new Element("details"));
		
		this.convertTestCasesToXML(testSuiteTag, suite, mapTS);
		
		Document xmlDocument = new Document(testSuiteTag);
		return xmlDocument;
	}

	/**
	 * Convert the sequence of TestCase objects into XML tags in Testlink format, according to the defined schema.
	 * @param testSuiteTag The parent tag.
	 * @param suite the test suite to be converted.
	 * @param mapTS The map of the test cases.
	 */
	private void convertTestCasesToXML(Element testSuiteTag, TestSuite suite, MapTestSuite mapTS) {
		for (int i = 0; i < suite.size(); i++) {
			Element testCaseTag = new Element("testcase");
			testCaseTag.setAttribute("internalid", "");
			//testCaseTag.setAttribute("name", "TC" + String.valueOf(i + 1));
			testCaseTag.setAttribute("name", "TC" + mapTS.getValueTestCase(suite.get(i)));
			
			testCaseTag.addContent(new Element("node_order"));
			testCaseTag.addContent(new Element("externalid"));
			testCaseTag.addContent(new Element("version"));
			testCaseTag.addContent(new Element("execution_type"));
			testCaseTag.addContent(new Element("importance"));
			testCaseTag.addContent(new Element("summary"));
			
			this.convertStepstoXML(testCaseTag, suite.get(i));
			testSuiteTag.addContent(testCaseTag);
		}
	}

	/**
	 * Convert a single TestCase object into a correspondent XML tags in Testlink format, according to the defined schema.
	 * @param testCaseTag The parent tag.
	 * @param testCase the test case to be converted.
	 */
	private void convertStepstoXML(Element testCaseTag, TestCase testCase) {
		Element stepsTag = new Element("steps");
		String preconditionsText = "";
		int stepCounter = 1;
		List<String> conditions = new ArrayList<String>();
		for (int i = 0; i < testCase.size(); i++) {
			InterfaceEdge edge = testCase.get(i);
			switch (edge.getType()) {
			case CONDITIONS:
				conditions.add(edge.getLabel());
			break;
			
			case STEPS:
				if(conditions.size() > 0){
					for (String condition : conditions) {
						preconditionsText += ("<p>#step_" + String.valueOf(stepCounter) + ": " + condition + " </p>");
					}
					conditions = new ArrayList<String>();
				}
				Element stepTag = new Element("step");
				stepTag.addContent(new Element("step_number").setText(String.valueOf(stepCounter)));
				stepTag.addContent(new Element("actions").setText("<p>" + edge.getLabel() + "<p>"));
				if(i + 1 < testCase.size()){
					if(testCase.get(i + 1).getType() == EdgeType.EXPECTED_RESULTS){
						stepTag.addContent(new Element("expectedresults").setText("<p>" + testCase.get(i + 1).getLabel() + "</p>"));
					}
				}
				stepTag.addContent(new Element("execution_type").setText(""));
				stepsTag.addContent(stepTag);
				stepCounter++;
			break;
			
			case DEFAULT:
				Element stepDefaultTag = new Element("step");
				stepDefaultTag.addContent(new Element("step_number").setText(String.valueOf(stepCounter)));
				stepDefaultTag.addContent(new Element("actions").setText("<p>" + edge.getLabel() + "<p>"));
				stepDefaultTag.addContent(new Element("execution_type").setText(""));
				stepsTag.addContent(stepDefaultTag);
				stepCounter++;
			break;

			default:
			
			break;
			}
		}
		testCaseTag.addContent(new Element("preconditions").setText(preconditionsText));
		testCaseTag.addContent(stepsTag);
	}
}
